#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import re,os
import pymysql
import collections
from string import Template

projectDir  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
projectName =os.path.basename(projectDir)

nullLib = "gopkg.in/guregu/null.v4"


tablePackage=projectName+'/core/dao/table'

daoRoot = projectName+"/core/dao/"
serviceRoot = projectName+"/core/service/"
routeRoot = projectName+"/http/"

dbname = 'power'
daoConfig = {
	daoRoot+"userdao":["Users","UserPrivates"],
	daoRoot+"graphdao":["Projects","ProjectUsers","GraphElementProtoFields","GraphFields"],
}

serviceConfig = {
	serviceRoot+"users":["Users","UserPrivates"],
	serviceRoot+"graphs":["Projects","ProjectUsers","GraphElementProtoFields","GraphFields"],
}
routeConfig = {
	routeRoot+"userroute":["Users","UserPrivates"],
	routeRoot+"graphroute":["Projects","ProjectUsers","GraphElementProtoFields","GraphFields"],
}



def getMysqlWrap():
	connection = pymysql.connect(host='localhost',
								 user='root',
								 password='123456',
								 db=dbname,
								 port=3306,
								 charset='utf8',
								 cursorclass=pymysql.cursors.DictCursor)
	return MysqlWrap(connection)

def capital(s) :
	if s:
		return s[0].upper()+s[1:]
	return s
def lower1(s) :
	if s:
		return s[0].lower()+s[1:]
	return s

def group(arr,key):
	r = {}
	for v in arr:
		k = v.get(key)
		if k not in r :
			r[k] = [v]
		else :
			r[k].append(v)
	return r
def groupOne(arr,key):
	r = {}
	for v in arr:
		r[v.get(key)] = v
	return r

def writeGoFile(content , packageFile, overwrite=False):
	f = os.path.join(os.path.dirname(projectDir) , packageFile)
	write(content , f, overwrite)
def write(content , file , overwrite=False) :
	d = os.path.dirname(file)
	if not os.path.exists(d) :
		print("mkdir "+d)
		os.makedirs(d)
	dst = os.path.join(d,file)
	if os.path.exists(dst) and not overwrite:
		print(dst +" has already exists,ignore it")
		return
	print("create "+dst)
	f = open(dst,"w+")
	f.write(content)
	f.close()


class MysqlWrap :
	def __init__(self ,con):
		self.con = con

	def listBySql(self,sql,args=None) :
		# print("listBySql sql:"+sql , "args:" , args)
		cur = self.con.cursor()
		cur.execute(sql , args)
		r = cur.fetchall()
		cur.close()
		return r

	def close(self):
		self.con.close()

# def getTableDesc():
# 	db = getCon()
# 	cursor = db.cursor()
# 	cursor.execute("show tables")
# 	results = cursor.fetchall()
# 	tables = {}
# 	for row in results:
# 		table = row["Tables_in_"+dbname]
# 		cursor.execute("show full columns from `"+table+"`")
# 		cols = cursor.fetchall()
# 		tables[table] = cols
# 	tableComments = {}
# 	cursor.execute("SELECT table_comment,table_name FROM INFORMATION_SCHEMA.TABLES where table_schema = '"+dbname+"'")
# 	results = cursor.fetchall()
# 	for row in results :
# 		tableComments[row["TABLE_NAME"]] = row["TABLE_COMMENT"]
# 	db.close()
# 	return (tables ,tableComments)


class Code:
	def __init__(self) :
		# self.code = ""
		pass
	def template(self ,template , args):
		return Template(template).substitute(args)
	# def n(self):
	# 	self.code +="\n"
	# def t(self):
	# 	self.code +="\t"
	def package(self , p):
		return "package "+p+"\n\n"
	def imports(self , packages):
		r = ""
		if len(packages)==0:
			return r
		r+="import (\n"
		for p in packages :
			r +='\t"'+p+'"\n'
		r +=")\n\n"
		return r

	 #fields:[{name:"Id" , type:"int" , comment:"identity"}]
	def struct(self , name , fields):
		s = "type "+name+" struct {\n"
		for field in fields :
			if 'comment' in field and field['comment']!="" :
				s+="\t//"+field['comment']+"\n"
			s+= "\t"
			if 'name' in field and field['name']!="" :
				s+= field['name']+" "
			if 'type' in field and field['type']!="" :
				s+=field['type']
			s+="\n"
		s+="}\n"
		return s
	def funcName(self, verb ,fields ,join="", prop = "By",sortFields=False):
		r = verb+prop
		fields = map(capital , fields)
		if sortFields :
			sorted(fields)
		r +=join.join(fields)
		return r

	# out: id int,name string
	def makeArgTypes(self , fields,table):
		r=""
		for f in fields:
			r+= f+" "+table.getFieldType(f)+","
		return r[0:len(r)-1]
	#out: .Int("id",id).String("name",name)
	def makeLogFields(self , fields,table):
		r=""
		for f in fields:
			r+= "."+capital(table.getFieldType(f))+'("'+f+'", '+f+")"
		return r
	

class DB(Code):
	def __init__(self , mysqlWrap,db) :
		super(DB,self).__init__()
		self.mysqlWrap = mysqlWrap
		self.db = db
		self.init()
	def init(self):
		# {'TABLE_CATALOG': 'def', 'TABLE_SCHEMA': '${projectName}', 'TABLE_NAME': 'Function', 
		# 'TABLE_TYPE': 'BASE TABLE', 'ENGINE': 'InnoDB', 'VERSION': 10, 'ROW_FORMAT': 'Dynamic',
		#  'TABLE_ROWS': 0, 'AVG_ROW_LENGTH': 0, 'DATA_LENGTH': 16384, 'MAX_DATA_LENGTH': 0, 'INDEX_LENGTH': 0, 'DATA_FREE': 0,
		#  'AUTO_INCREMENT': 1, 'CREATE_TIME': datetime.datetime(2020, 5, 22, 6, 53, 28), 'UPDATE_TIME': None, 'CHECK_TIME': None, 
		# 'TABLE_COLLATION': 'utf8mb4_general_ci', 'CHECKSUM': None, 'CREATE_OPTIONS': '', 'TABLE_COMMENT': ''}
		self.tableInfos = self.mysqlWrap.listBySql("select * from INFORMATION_SCHEMA.TABLES where table_schema='"+self.db+"'")
		self.tables = {}
		for t in self.tableInfos:
			self.tables[t["TABLE_NAME"]] = Table(self.mysqlWrap , self.db , t['TABLE_NAME'])
		self.tableNames = self.tables.keys()
		sorted(self.tables)


	def code(self,package=""):
		r = self.package(package)
		if self.hasNullField():
			r+= self.imports([nullLib])
		for t in self.tableNames:
			r+= self.tables[t].code()+"\n"
		return r
	def code_tables(self):
		r = ""
		for t in self.tables:
			r+= +"\n\n"
		return r
	def hasNullField(self):
		for t in self.tables:
			if self.tables[t].hasNullField() :
				return True
		return False


	def writeTable(self , tablePackage):
		writeGoFile(self.code(os.path.basename(tablePackage)) ,tablePackage+"/Table.go",True)

	def writeDao(self,daoConfig):
		for p ,daos in daoConfig.items():
			for st in daos:
				t = st[0:len(st)-1]
				d = Dao(p , self.tables[t])
				writeGoFile(d.code(),p+"/"+st+".go")
				writeGoFile(d.codeTest(),p+"/"+st+"_test.go")
	def writeService(self,serviceConfig):
		for p,services in serviceConfig.items():
			daoPackage = os.path.basename(p)
			daoPackage = daoPackage[0:len(daoPackage)-1]+"dao"
			for serviceName in services :
				s = Service(p , serviceName , daoPackage , serviceName,self.tables[serviceName[0:len(serviceName)-1]])
				writeGoFile(s.code(),p+"/"+serviceName+".go")
				writeGoFile(s.codeTest(),p+"/"+serviceName+"_test.go")

	def writeRoute(self,config):
		for p,routes in config.items():
			rp = os.path.basename(p)
			rp = rp[0:len(rp)-len('route')]
			for st in routes :
				r = Route(p , rp+"s",rp+"dao",st,self.tables[st[0:len(st)-1]])
				writeGoFile(r.code(),p+"/"+st+".go")
				# writeGoFile(s.codeTest(),p+"/"+serviceName+"_test.go")


class Table(Code) :
	def __init__(self , mysqlWrap,db , table):
		super(Table,self).__init__()
		self.mysqlWrap = mysqlWrap
		self.db = db
		self.table = table
		self.name = capital(table)
		self.init()
	def init(self):
		#[{'Field': 'id', 'Type': 'int', 'Collation': None, 'Null': 'NO', 
		# 'Key': 'PRI', 'Default': None, 'Extra': 'auto_increment', 'Privileges': 'select,insert,update,references',
		# 'Comment': ''}]
		self.cols = self.mysqlWrap.listBySql("show full columns from `"+self.table+"`")#列
		#[{'Table': 'User', 'Non_unique': 0, 'Key_name': 'PRIMARY', 'Seq_in_index': 1, 
		# 'Column_name': 'id', 'Collation': 'A', 'Cardinality': 0, 'Sub_part': None, 'Packed': None, 'Null': '', 'Index_type': 'BTREE', 
		# 'Comment': '', 'Index_comment': '', 'Visible': 'YES', 'Expression': None}]
		self.indexs = self.mysqlWrap.listBySql("show index from `"+self.table+"`")#索引
		#{'Name': 'User', 'Engine': 'InnoDB', 'Version': 10, 'Row_format': 'Dynamic', 'Rows': 0,
		#  'Avg_row_length': 0, 'Data_length': 16384, 'Max_data_length': 0, 'Index_length': 0, 
		# 'Data_free': 0, 'Auto_increment': 2, 'Create_time': datetime.datetime(2020, 7, 31, 3, 9, 18), 
		# 'Update_time': datetime.datetime(2020, 7, 31, 3, 43, 55), 'Check_time': None, 'Collation': 'utf8mb4_general_ci',
		#  'Checksum': None, 'Create_options': '', 'Comment': ''}
		self.info = self.mysqlWrap.listBySql("show table status where Name='"+self.table+"'")[0] #表信息
		# [{'CONSTRAINT_CATALOG': 'def', 'CONSTRAINT_SCHEMA': '${projectName}', 'CONSTRAINT_NAME': 'tuTeamId', 'TABLE_CATALOG': 'def', 
		# 'TABLE_SCHEMA': '${projectName}', 'TABLE_NAME': '${tableName}', 'COLUMN_NAME': 'teamId', 'ORDINAL_POSITION': 1, 'POSITION_IN_UNIQUE_CONSTRAINT': 1, 
		# 'REFERENCED_TABLE_SCHEMA': '${projectName}', 'REFERENCED_TABLE_NAME': 'Team', 'REFERENCED_COLUMN_NAME': 'id'}]
		self.fks = self.mysqlWrap.listBySql("select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where table_schema='"+self.db+"' and table_name='"+self.table+"' and referenced_table_name is not null") # 外键
		self.initIndexFields() # except primary key
		self.initColNames()
		self.indexMap = group(self.indexs , "Column_name")
		self.indexKey = group(self.indexs , "Key_name")
		self.fksMap = groupOne(self.fks,"COLUMN_NAME")
		self.colMap = groupOne(self.cols,"Field")
		self.initUniqueIndexs()
		self.initNormalIndexs()
		self.initIndexMethods()
	def initColNames(self):
		r = []
		for v in self.cols :
			r.append(v['Field'])
		sorted(r)
		self.colNames = r

	def initUniqueIndexs(self):
		r = {}
		for k,v in self.indexKey.items():
			arr = []
			if v[0]['Non_unique']!=0 or v[0]['Key_name'] =='PRIMARY':
				continue
			for i in v :
				field = i['Column_name'] 
				col = self.colMap[field]
				arr.append({"field":field,"type":self.mapType(col["Type"])})
			r[v[0]['Key_name']] = arr
		self.uniqueIndexs = r
	def initNormalIndexs(self):
		r = {}
		for k,v in self.indexKey.items():
			if v[0]['Non_unique']==0 or v[0]['Key_name'] =='PRIMARY':
				continue
			arr = []
			for i in v :
				field = i['Column_name'] 
				col = self.colMap[field]
				arr.append({"field":field,"type":self.mapType(col["Type"])})
			r[v[0]['Key_name']] = arr
		self.normalIndexs = r
	def initIndexMethods(self):
		r = []
		for k,v in self.indexKey.items() :
			if "PRIMARY" == k.upper() :
				continue
			item = {"Key_name" : k,"Non_unique":v[0]['Non_unique'],"Column_names":[]}
			for i in v:
				item["Column_names"].append(i["Column_name"])
			sorted(item['Column_names'])
			r.append(item)
		self.indexMethods = r
	def initIndexFields(self):
		r = set()
		for v in self.indexs:
			if v['Key_name'] =='PRIMARY':
				continue
			r.add(v['Column_name'])
		r = list(r)
		sorted(r)
		self.indexFields = r
			
	def code(self):
		fields = []
		for col in self.cols :
			name = capital(col["Field"])
			indexes = self.indexMap.get(col["Field"])
			fk = self.fksMap.get(col["Field"])
			field = {"name":name, }
			field['type'] = self.mapType(col["Type"] ,col["Null"]=="YES" , col["Comment"] )
			field['comment'] = name+" field:"+col["Field"]+" "+ col["Type"] +";"
			if indexes :
				for index in indexes:
					field['comment'] += " "+ ('unique' if index['Non_unique']==0  else "normal")+" index:"+index['Key_name']
				field['comment']+=";"
			if fk :
				field['comment'] +=" fk:"+fk['CONSTRAINT_NAME']+" ref:"+fk['REFERENCED_TABLE_NAME']+"."+fk['REFERENCED_COLUMN_NAME']
			field['comment'] += col["Comment"]
			fields.append(field)
		comment = "//"+ self.name+" table:"+self.info["Name"]+" Engine: "+self.info['Engine']+" Collation: "+self.info['Collation']
		if self.info["Comment"]!="":
			comment +="\n//"+self.info["Comment"]
		comment +="\n"
		return comment + self.struct(self.name, fields)

	def getFieldType(self,field) :
		c = self.colMap[field]
		return self.mapType(c['Type'],False, c['Comment'])

	def mapType(self,mysqlType , nullable=False, comment=""):
		m = re.match(r"type:(\w+)" , comment)
		if m :
			t= m.group(1)
			if t != "" :
				return t
		if -1!= mysqlType.find("bigint") :
			if nullable :
				return "null.Int"
			return "int64"
		if -1!= mysqlType.find("tinyint") :
			if nullable :
				return "null.Bool"
			return "bool"
		if -1!= mysqlType.find("float") :
			if nullable :
				return "null.Float"
			return "float32"
		if -1!= mysqlType.find("int") :
			if nullable :
				return "null.Int"
			return "int"
		if -1!= mysqlType.find("date") :
			if nullable :
				return "null.Time"
			return "time.Time"
		# if -1!= mysqlType.find("char") or -1!= mysqlType.find("text") :
		# 	if nullable :
		# 		return "null.String"
		# 	return "string"
		if nullable:
			return "null.String"
		return "string"
	
	def hasNullField(self):
		for col in self.cols :
			if col["Null"]=="YES":
				return True
		return False



daoTpl='''package ${packageName}

import (
	"${projectName}/core/dao/table"
	"${projectName}/core/localwrap"
	"${projectName}/core/service/common"

	"github.com/RocksonZeta/wrap/redised"
	"github.com/RocksonZeta/wrap/utils/sutil"
	"github.com/RocksonZeta/wrap/utils/timeutil"
)

const table${tableName} = "${tableName}"
const table${tableName}Id = "Id"

var log${structName} = localwrap.GetLogger().Fork("${projectName}/core/dao/${packageName}", "${structName}")

//${structName} 
type ${structName} struct {
	*redised.RedisedMysql
}

type ${structName}QueryParam struct {
	common.Page
	Name string
}

//Query 根据条件查询
func (s ${structName}) Query(param ${structName}QueryParam) ([]table.${tableName}, int64) {
	log${structName}.Debug().Func("Query").Interface("param", param).Send()
	wsql := " from ${tableName} where 1=1"
	if param.Name != "" {
		param.Name = "%" + param.Name + "%"
		wsql += " and name like :Name"
	}
	ssql := "select *" + wsql + " order by ct desc" + param.Limit()
	csql := "select count(*)" + wsql
	var r []table.${tableName}
	s.Mysql.Select(&r, ssql, param)
	return r, s.Mysql.SelectInt(csql, param)
}

func (s ${structName}) Get(id int) table.${tableName} {
	log${structName}.Debug().Func("Get").Int("id", id).Send()
	var r table.${tableName}
	s.RedisedMysql.Get(&r, table${tableName}, table${tableName}Id, id)
	return r
}

func (s ${structName}) List(ids []int) []table.${tableName} {
	log${structName}.Debug().Func("List").Ints("ids", ids).Send()
	var r []table.${tableName}
	s.RedisedMysql.List(&r, table${tableName}, table${tableName}Id, ids)
	return r
}
func (s ${structName}) ListAsMap(ids []int) map[int]table.${tableName} {
	log${structName}.Debug().Func("ListAsMap").Ints("ids", ids).Send()
	l := s.List(ids)
	r := make(map[int]table.${tableName}, len(l))
	for _, v := range l {
		r[v.Id] = v
	}
	return r
}

func (s ${structName}) Delete(id int) {
	log${structName}.Debug().Func("Delete").Int("id", id).Send()
	old := s.Get(id)
	s.Mysql.Delete(table${tableName}, table${tableName}Id, id)
	s.ClearCache(old)
}
'''

indexFuncTpl = '''func (s ${structName}) ${funcName}(${args}) ${list}table.${tableName} {
	log${structName}.Debug().Func("${funcName}")${logFields}.Send()
	var r ${list}table.${tableName}
	s.RedisedMysql.${fetchMethod}(&r, table${tableName}, table${tableName}Id, map[string]interface{}{${argKv}})
	return r
}'''

daoTestTpl = '''
package ${packageName}_test

import (
	"${projectName}/core/dao/${packageName}"
	"${projectName}/core/localwrap"
	"testing"

	"github.com/stretchr/testify/suite"
)

type ${structName}Suite struct {
	suite.Suite
	x *${packageName}.${structName}
}

func (s *${structName}Suite) SetupSuite() {
}
func (s *${structName}Suite) SetupTest() {
	s.x = &${packageName}.${structName}{RedisedMysql: localwrap.GetRedisedMysql()}
}
func (s *${structName}Suite) TearDownTest() {
	s.x.Close()
}
func Test${structName}Suite(t *testing.T) {
	suite.Run(t, new(${structName}Suite))
}

func (s *${structName}Suite) TestGet() {
	v := s.x.Get(1)
	s.Assert().Equal(1, v.Id)
}

'''

class Dao(Code):
	def __init__(self , packagePath,table):
		super(Dao,self).__init__()
		self.table = table
		self.packagePath = packagePath
		ps = packagePath.split("/")
		self.projectName = ps[0]
		self.packageName = ps[-1]
		self.tableName = table.table
		self.structName = capital(self.tableName)+"s"
	def params(self) :
		return {"projectName":self.projectName,"packageName":self.packageName,"tableName":self.tableName,"structName":self.structName}
	def code(self):
		params =self.params()
		print("code params:" , params)
		r = self.template(daoTpl , params)
		r += "\n"
		r += self.genClearCacheFunc()
		r += self.genIndexMethods()
		r += self.genUpdateFunc()
		r += self.genAddFunc()
		return r
	def genIndexMethods(self):
		r =""
		for v in self.table.indexMethods:
			# self.struct
			r+=self.genIndexMethod(v)+"\n"
		return r
	def genIndexMethod(self , i):
		fields = i['Column_names']
		params = self.params()
		params["list"]=""
		if i['Non_unique']:
			params["funcName"] = self.funcName('List' , fields)
			params["fetchMethod"] = "ListByKvs"
			params["list"]="[]"
		else:
			params["funcName"] = self.funcName('Get' , fields)
			params["fetchMethod"] = "GetByKvs"
		params["args"] = self.makeArgs(fields)
		params["logFields"] = self.makeLogFields(fields)
		params["argKv"] = self.makeArgKv(fields)

		r = self.template(indexFuncTpl , params)
		return r
	
	def makeArgs(self , fields):
		r=""
		for f in fields:
			r+= f+" "+self.table.getFieldType(f)+","
		return r[0:len(r)-1]
	def makeLogFields(self , fields):
		r=""
		for f in fields:
			r+= "."+capital(self.table.getFieldType(f))+'("'+f+'", '+f+")"
		return r
	def makeArgKv(self , fields , prefix=''):
		r=""
		for f in fields:
			r+= '"' +f+'": '
			if ''!=prefix:
				r += prefix+"."+capital(f)
			else :
				r+=f
			r+=', '
		return r[0:len(r)-2]

	def genClearCacheFunc(self):
		params = self.params()
		tpl='''func (s ${structName}) ClearCache(old table.${tableName}) {
	log${structName}.Debug().Func("ClearCache").Int("id", old.Id).Send()
	s.Redis.Del(s.KeyFn(table${tableName}, old.Id))
'''
		r = self.template(tpl ,params)
		for v in self.table.indexMethods:
			r+= self.template('\ts.Redis.Del(s.KVFn(table${tableName}, map[string]interface{}{'+self.makeArgKv(v['Column_names'] , 'old')+'}))\n', params)
		r +="}\n"
		return r
	def genUpdateFunc(self):
		fields = []
		for i in self.table.colNames :
			if i not in self.table.indexFields and i!="ct":
				f = {"name":capital(i),'type':self.table.getFieldType(i)}
				fields.append(f)
		r = self.struct(self.structName+"UpdateParam",fields)+'\n'
		tpl='''func (s ${structName}) Update(param ${structName}UpdateParam) {
	log${structName}.Debug().Func("Update").Interface("param", param).Send()
	s.Mysql.Patch(table${tableName}, table${tableName}Id, param)
	s.Redis.Del(s.KeyFn(table${tableName}, param.Id))
	// s.ClearCache(s.Get(param.Id)) if no index updated
}\n
'''
		params = self.params()
		r += self.template(tpl , params)
		return r

	def genAddFunc(self):
		fields = []
		for i in self.table.colNames :
			if i not in ["id",'ct']:
				f = {"name":capital(i),'type':self.table.getFieldType(i)}
				fields.append(f)
		r = self.struct(self.structName+"AddParam",fields)+'\n'
		tpl='''func (s ${structName}) Add(param ${structName}AddParam) table.${tableName} {
	log${structName}.Debug().Func("Add").Interface("param", param).Send()
	var t table.${tableName}
	sutil.Copy(param, &t)
	t.Ct = timeutil.Now()
	s.Mysql.AddTable(t)
	s.Mysql.Insert(&t)
	s.ClearCache(t)
	return t
}
'''
		params = self.params()
		r += self.template(tpl , params)
		return r

	def codeTest(self):
		r = self.template(daoTestTpl , self.params())
		return r


serviceTpl = '''package ${packageName}

import (
	"${projectName}/core/dao/table"
	"${projectName}/core/dao/${daoPackage}"
	"${projectName}/core/localwrap"
)

var log${serviceName} = localwrap.GetLogger().Fork("${packagePath}", "${serviceName}")

type ${serviceName} struct{}

func (s ${serviceName}) Query(param ${daoPackage}.${daoStruct}QueryParam) ([]table.${table}, int64) {
	log${serviceName}.Debug().Func("Query").Interface("param", param).Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	return ${daoPackage}.${daoStruct}{RedisedMysql: d}.Query(param)
}

func (s ${serviceName}) Get(id int) table.${table} {
	log${serviceName}.Debug().Func("Get").Int("id", id).Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	return ${daoPackage}.${daoStruct}{RedisedMysql: d}.Get(id)
}

func (s ${serviceName}) Add(param ${daoPackage}.${daoStruct}AddParam) table.${table} {
	log${serviceName}.Debug().Func("Add").Interface("param", param).Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	return ${daoPackage}.${daoStruct}{RedisedMysql: d}.Add(param)
}

func (s ${serviceName}) Update(param ${daoPackage}.${daoStruct}UpdateParam) {
	log${serviceName}.Debug().Func("Update").Interface("param", param).Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	${daoPackage}.${daoStruct}{RedisedMysql: d}.Update(param)
}

func (s ${serviceName}) Delete(id int) {
	log${serviceName}.Debug().Func("Delete").Int("id", id).Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	${daoPackage}.${daoStruct}{RedisedMysql: d}.Delete(id)
}
'''

serviceTestTpl = '''package ${packageName}_test

import (
	"${projectName}/core/service/${packageName}"
	"testing"

	"github.com/stretchr/testify/suite"
)

type ${serviceName}Suite struct {
	suite.Suite
	x *${packageName}.${serviceName}
}

func (s *${serviceName}Suite) SetupSuite() {
	s.x = &${packageName}.${serviceName}{}
}
func (s *${serviceName}Suite) SetupTest() {
}
func (s *${serviceName}Suite) TearDownTest() {
}
func Test${serviceName}Suite(t *testing.T) {
	suite.Run(t, new(${serviceName}Suite))
}

func (s *${serviceName}Suite) TestGet() {
	v := s.x.Get(1)
	s.Assert().Equal(1, v.Id)
}
'''

serviceIndexFuncTpl ='''
//${funcName}
func (s ${serviceName}) ${funcName}(${argTypes}) ${list}table.${table} {
	log${serviceName}.Debug().Func("${funcName}")${logFields}.Send()
	d := localwrap.GetRedisedMysql()
	defer d.Close()
	return ${daoPackage}.${daoStruct}{RedisedMysql: d}.${funcName}(${args})
}
'''

class Service(Code):
	def __init__(self,packagePath,serviceName,daoPackage,daoStruct,table):
		super(Service,self).__init__()
		ps = packagePath.split("/")
		self.table = table
		self.params = {"packagePath":packagePath,"projectName":ps[0],"packageName":ps[-1] ,"serviceName":serviceName,"daoPackage":daoPackage,"daoStruct":daoStruct,"table":table.table}

	def code(self):
		r = self.template(serviceTpl , self.params)
		r += self.genIndexMethods()
		return r

	def codeTest(self):
		return self.template(serviceTestTpl , self.params)

	def genIndexMethods(self):
		r =""
		for v in self.table.indexMethods:
			# self.struct
			r+=self.genIndexMethod(v)
		return r
	def genIndexMethod(self , i):
		fields = i['Column_names']
		params = self.params
		params['list']='[]'
		if i['Non_unique']:
			params["funcName"] = self.funcName('List' , fields)
		else:
			params["funcName"] = self.funcName('Get' , fields)
			params['list']=''
		params["args"] = ", ".join(fields)
		params["argTypes"] = self.makeArgTypes(fields,self.table)
		params["logFields"] = self.makeLogFields(fields,self.table)

		r = self.template(serviceIndexFuncTpl , params)
		return r

routeTpl = '''package ${packageName}

import (
	"${projectName}/core/dao/${daoPackage}"
	"${projectName}/core/localwrap"
	"${projectName}/core/service/${servicePackage}"
	"${projectName}/http/routes"

	"github.com/RocksonZeta/irisx"
	"github.com/kataras/iris/v12"
)

var log${serviceName} = localwrap.GetLogger().Fork("${projectName}/http/${packageName}", "Route${serviceName}")

//Route{serviceName} 
func Route${serviceName}(party iris.Party) {
	party.Get("/", func(ctx iris.Context) {
		c := ctx.(*irisx.Context)
		id := c.CheckQuery("id").NotEmpty().Int(0)
		uid := c.GetUidInt()
		log${serviceName}.Debug().Str("path", c.Path()).Int("id", id).Int("uid", uid).Send()
		r := ${servicePackage}.${serviceName}{}.Get(id)
		c.Ok(r)
	})
	party.Get("/list", func(ctx iris.Context) {
		c := ctx.(*irisx.Context)
		uid := c.GetUidInt()
		log${serviceName}.Debug().Str("path", c.Path()).Int("uid", uid).Send()
		r := ${servicePackage}.${serviceName}{}.ListByUid(uid)
		c.Ok(r)
	})
	party.Post("/update", func(ctx iris.Context) {
		c := ctx.(*irisx.Context)
		var param ${daoPackage}.${serviceName}UpdateParam
		param.Id = c.CheckBody("id").NotBlank().Int(0)
		param.Name = c.CheckBody("name").NotBlank().String()
		log${serviceName}.Debug().Str("path", c.Path()).Interface("param", param).Send()
		routes.Check(c)
		${servicePackage}.${serviceName}{}.Update(param)
		c.Ok(nil)
	})
	party.Post("/delete", func(ctx iris.Context) {
		c := ctx.(*irisx.Context)
		id := c.CheckBody("id").NotBlank().Int(0)
		log${serviceName}.Debug().Str("path", c.Path()).Int("uid", c.GetUidInt()).Send()
		routes.Check(c)
		${servicePackage}.${serviceName}{}.Delete(id)
		c.Ok(nil)
	})
}
'''

routeTestTpl = '''
package ${packageName}_test

import (
	"${projectName}/http"
	"testing"

	"github.com/kataras/iris/v12/httptest"
	"github.com/stretchr/testify/suite"
)

type ${serviceName}Suite struct {
	suite.Suite
	app *httptest.Expect
}

func (s *${serviceName}Suite) SetupSuite() {
	s.app = httptest.New(s.T(), http.NewApp(), httptest.URL("http://domain.com"))
}
func (s *${serviceName}Suite) SetupTest() {
}
func (s *${serviceName}Suite) TearDownTest() {
}
func Test${serviceName}Suite(t *testing.T) {
	suite.Run(t, new(${serviceName}Suite))
}

func (s *${serviceName}Suite) TestGet() {
	s.app.GET("/").Expect().Status(httptest.StatusOK).JSON().Path("State").Equal(0)
}
'''

class Route(Code):
	def __init__(self,packagePath,servicePackage,daoPackage,serviceName,table):
		super(Route,self).__init__()
		ps = packagePath.split("/")
		self.table = table
		self.params = {"packagePath":packagePath,"projectName":ps[0],"packageName":ps[-1] ,"serviceName":serviceName,"servicePackage":servicePackage,"daoPackage":daoPackage,"table":table.table}

	def code(self):
		return self.template(routeTpl , self.params)

	def codeTest(self):
		return self.template(routeTestTpl , self.params)




mysqlWrap = getMysqlWrap()

db = DB(mysqlWrap ,dbname)

mysqlWrap.close()

db.writeTable(tablePackage)
db.writeDao(daoConfig)
db.writeService(serviceConfig)
db.writeRoute(routeConfig)